// bslmf_voidtype.h                                                   -*-C++-*-
#ifndef INCLUDED_BSLMF_VOIDTYPE
#define INCLUDED_BSLMF_VOIDTYPE

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a helper for implementing SFINAE-based metafunctions.
//
//@CLASSES:
//  bsl::void_t:     alias template to help create SFINAE contexts in C++11
//  bslmf::VoidType: class template to emulate 'bsl::void_t' in C++03
//
//@MACROS:
//  BSLMF_VOIDTYPE:  helper macro for SFINAE-based metafunctions
//  BSLMF_VOIDTYPE2: helper macro for SFINAE-based metafunctions
//  BSLMF_VOIDTYPES: helper macro for SFINAE-based metafunctions
//
//@SEE_ALSO: bslmf_resulttype
//
//@DESCRIPTION: This component provides the alias template 'bsl::void_t', as
// specified by the C++14 standard, and a metafunction, 'bslmf::VoidType', to
// emulate the same functionality for older compilers that do not support alias
// templates, which are first specified by the C++11 standard.  It further
// provides 3 macros, 'BSLMF_VOIDTYPE/2/S', that provide a consistent way to
// use the alias template where supported and the class template otherwise.
//
// All forms of the metafunction, however it is written, produce the result
// type 'void'.  The usefulness of this do-nothing metafunction is that, when
// it is instantiated, all of its template arguments must be valid.  By putting
// the template instantiation in a SFINAE context, any use of template
// parameters that name invalid dependent types are discarded by the compiler
// as non-viable.  Thus, 'VoidType' is most commonly used to build
// metafunctions that test for the existence of a specific nested data type
// (see {Usage}).
//
// The 'bslmf::VoidType' class template is intended to provide functionality
// identical to the C++14 metafunction 'std::void_t', but without using C++11
// alias templates.  A use, in C++14-compliant code, of:
//..
//  std::void_t<T1, T2, ...>
//..
// can be replaced, in BDE-compliant code using any version of standard C++,
// by:
//..
//  typename bslmf::VoidType<T1, T2, ...>::type
//..
//
///Macro Reference
///---------------
// This section documents the preprocessor macros defined in this component.
//
///Macros for type-dependant SFINAE checks in any C++ dialect
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The following macros are for use only in a type-dependent context.  They all
// expand to a type expression that uses either 'bsl::void_t<ARGS>' if alias
// templates are supported by the current compiler, and to
// 'typename BloombergLP::bslmf::VoidType<ARGS>::type' otherwise.  The alias
// template form is preferred, as it consumes fewer resources while compiling
// code; the compiled code will be equivalent, whichever implementation is
// chosen.  These macros support writing simple code that uses the preferred
// idiom supported by the current tool chain.  Note that code targeting only
// C++11 or later can use 'bsl::void_t' directly with no loss of efficiency or
// generality.  These macros are needed only to support older C++03 tool
// chains.
//
// The three macros support one, two, or many arguments, and are otherwise
// identical.  The reason for three macros is that the overwhelmingly common
// use cases need only one, rarely two, type expressions, so for legacy
// compilers that do not support variadic templates, we can use a simpler class
// template that has fewer type parameters.
//
//: 'BSLMF_VOIDTYPE( TYPE_EXPRESSION )':
//:     This macro will expand into a type expression that aliases 'void' if
//:     the "TYPE EXPRESSION" is valid, and will fail to expand in a SFINAE
//:     friendly manner if the type expressions is not valid.
//
//: 'BSLMF_VOIDTYPE2( TYPE_EXPRESSION_1, TYPE_EXPRESSION_2 )':
//:     This macro will expand into a type expression that aliases 'void' if
//:     each "TYPE EXPRESSION" is valid, and will fail to expand in a SFINAE
//:     friendly manner if either of the type expressions is not valid.
//
//: 'BSLMF_VOIDTYPES( TYPE_EXPRESSIONS... )':
//:     This macro will expand into a type expression that aliases 'void' if
//:     each "TYPE EXPRESSION" is valid, and will fail to expand in a SFINAE
//:     friendly manner if any of the type expressions is not valid.
//
///Additional concerns when using old compilers
///--------------------------------------------
// When compiling with a C++03 tool chain, the 'typename' keyword in the macro
// expansion, used to extract the nested 'type' from the class template, is not
// valid syntax unless the whole type expression is type (or value) dependent.
// Such use will produce an error, even if the supplied type expression is
// valid.  E.g., 'BSLMF_VOIDTYPE(void)' will always be a (non-SFINAE) error in
// C++03, but is perfectly valid in C++11.  Be sure to test your code with a
// C++03 build when deploying these macros.
//
// The templates and macros in this component aid in creating SFINAE conditions
// in a conforming C++03 manner.  However, several of the compilers still in
// production have significant issues in their handling of SFINAE.  For
// example, the Solaris CC compiler (prior to the 12.4 release) is very
// forgiving and will accept most invalid code without creating a SFINAE
// failure.  Idiomatic use of this component for compile-time reflection to
// detect named members of a class is known to work.  Other uses should be
// carefully tested before deployment.
//
///Usage
///-----
// In this section we show intended use of this component.
//
///Usage Example 1
///- - - - - - - -
// In this example, we demonstrate the use of 'VoidType' to determine whether a
// given type 'T' has a member type 'T::iterator'.  Our goal is to create a
// metafunction, 'HasIteratorType', such that 'HasIteratorType<T>::k_VALUE' is
// 'true' if 'T::iterator' is a valid type and 'false' otherwise.  This example
// is adapted from the paper proposing 'std::void_t' for the C++ Standard,
// N3911.
//
// First, we define the base-case metafunction that returns 'false':
//..
//  template <class TYPE, class = void>
//  struct HasIteratorType {
//      enum { k_VALUE = false };
//  };
//..
// Then, we create a partial specialization that uses 'VoidType' to probe for
// 'T::iterator':
//..
//  template <class TYPE>
//  struct HasIteratorType<TYPE, BSLMF_VOIDTYPE(typename TYPE::iterator)> {
//      enum { k_VALUE = true };
//  };
//..
// Now, we define a class that has an 'iterator' member and apply
// 'HasIteratorType' to it:
//..
//  struct WithIterator {
//      typedef short *iterator;
//  };
//
//  void usageExample1()
//  {
//      assert(true == HasIteratorType<WithIterator>::k_VALUE);
//..
// As 'WithIterator::iterator' is a valid type,
// 'BSLMF_VOIDTYPE(typename TYPE::iterator)' will be 'void' and the second
// 'HasIteratorType' template will be more specialized than the primary
// template, thus yielding a 'k_VALUE' of 'true'.
//
// Finally, we try to instantiate 'HasIteratorType<int>'.  Any use of
// 'BSLMF_VOIDTYPE(TYPE::iterator)' will result in a substitution failure.
// Fortunately, the Substitution Failure Is Not An Error (SFINAE) rule applies
// and the partial specialization is eliminated from consideration, resulting
// in the primary template being instantiated and yielding a 'k_VALUE' of
// 'false':
//..
//      assert(false == HasIteratorType<int>::k_VALUE);
//  }
//..
//
///Usage Example 2
///- - - - - - - -
// This example demonstrates the use of 'VoidType' to probe for more than one
// type at once.  As in the previous example, we are defining a metafunction.
// We'll define 'IsTraversable<T>::k_VALUE' to be 'true' if 'T::iterator' and
// 'T::value_type' both exist.  First, we define a primary template that always
// yields 'false':
//..
//  template <class TYPE, class = void>
//  struct IsTraversable {
//      enum { k_VALUE = false };
//  };
//..
// Then, we create a partial specialization that uses 'BSLMF_VOIDTYPE2' with
// two parameters:
//..
//  template <class TYPE>
//  struct IsTraversable<TYPE,
//                       BSLMF_VOIDTYPE2(typename TYPE::iterator,
//                                       typename TYPE::value_type)> {
//      enum { k_VALUE = true };
//  };
//..
// Now, we define a type that meets the requirements for being traversable:
//..
//  struct MyTraversable {
//      typedef int  value_type;
//      typedef int *iterator;
//  };
//..
// Finally, the 'IsTraversable' metafunction yields 'true' for 'Traversable'
// but not for either 'WithIterator', which lacks a 'value_type' member, nor
// 'int', which lacks both 'iterator' and 'value_type' members:
//..
//  int usageExample2()
//  {
//      assert(true  == IsTraversable<MyTraversable>::k_VALUE);
//      assert(false == IsTraversable<WithIterator>::k_VALUE);
//      assert(false == IsTraversable<int>::k_VALUE);
//  }
//..

#include <bslscm_version.h>

#include <bsls_compilerfeatures.h>

namespace BloombergLP {
namespace bslmf {

                        // =======================
                        // class template VoidType
                        // =======================

#if defined(BSLS_COMPILERFEATURES_SUPPORT_VARIADIC_TEMPLATES)
template <class ...>
#else
template <class T1  = void, class T2  = void, class T3  = void,
          class T4  = void, class T5  = void, class T6  = void,
          class T7  = void, class T8  = void, class T9  = void,
          class T10 = void, class T11 = void, class T12 = void,
          class T13 = void, class T14 = void>
#endif
struct VoidType {
    // Metafunction that always yields 'type' 'void' for any well-formed list
    // of type parameters.  This metafunction is useful when using SFINAE to
    // probe for well-formed types.

    // PUBLIC TYPES
    typedef void type;
};

}  // close package namespace
}  // close enterprise namespace


#if defined(BSLS_COMPILERFEATURES_SUPPORT_ALIAS_TEMPLATES) &&   \
    defined(BSLS_COMPILERFEATURES_SUPPORT_VARIADIC_TEMPLATES)
namespace bsl {

template <class...>
using void_t = void;
}  // close namespace bsl

# define BSLMF_VOIDTYPE(ARG)  bsl::void_t<     ARG   >
# define BSLMF_VOIDTYPE2(...) bsl::void_t<__VA_ARGS__>
# define BSLMF_VOIDTYPES(...) bsl::void_t<__VA_ARGS__>

#else
// Define a couple of implementation-private classes with smaller template
// parameter lists, for efficient use on C++03 compilers.  Note that the
// preferred interface for portable C++03/11 code is to use the three
// 'VOIDTYPE' macros, rather than the class templates directly.

namespace BloombergLP {
namespace bslmf {

template <class T1>
struct VoidType_1 {
    // Metafunction that always yields 'type' 'void' for any well-formed type
    // parameter.  This metafunction is useful when using SFINAE to probe for
    // well-formed types.  Note that this metafunction is not intended for
    // direct user consumption, but rather as an implementation detail for the
    // 'BSLMF_VOIDTYPE' macro.

    // PUBLIC TYPES
    typedef void type;
};

template <class T1, class T2>
struct VoidType_2 {
    // Metafunction that always yields 'type' 'void' for any well-formed list
    // of type parameters.  This metafunction is useful when using SFINAE to
    // probe for well-formed types.  Note that this metafunction is not
    // intended for direct user consumption, but rather as an implementation
    // detail for the 'BSLMF_VOIDTYPE2' macro.

    // PUBLIC TYPES
    typedef void type;
};

}  // close package namespace
}  // close enterprise namespace

# define BSLMF_VOIDTYPE(ARG)                                                  \
                   typename BloombergLP::bslmf::VoidType_1<     ARG   >::type
# define BSLMF_VOIDTYPE2(...)                                                 \
                   typename BloombergLP::bslmf::VoidType_2<__VA_ARGS__>::type
# define BSLMF_VOIDTYPES(...)                                                 \
                   typename BloombergLP::bslmf::VoidType<__VA_ARGS__>::type
#endif  // supports C++11 API directly

#endif

// ----------------------------------------------------------------------------
// Copyright 2019 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
